package main;

import java.util.Date;

import utils.MatrixLoader;
import barrier.BarrierType;

/**
 * Group 9 (Yoni Seroussi, Roei Raviv): Simple Barriers Sense-Reversing Barrier
 * Combining Tree Barrier Static Tree Barrier
 */
public class Main {
    private static final String STR_ITERS = "ITERS=";
    private static final String STR_NTHREADS = "NTHREADS=";
    private static final String STR_COUNTTO = "COUNTTO=";
    private static final String STR_BARTYPE = "BARTYPE=";
    private static final String STR_LOCALMEM = "LOCALMEM=";
    private static final String STR_MATRIX_FILE = "MATFILE=";

    private static final int DEFAULT_NUM_OF_THREADS = 4;
    private static final int DEFAULT_COUNT_TO = 100;// 00;
    private static final int DEFAULT_MAX_ITER = 1;//35;
    private static final boolean DEFAULT_IS_LOCAL_MEM = false;
    
    private static int _numOfThreads = DEFAULT_NUM_OF_THREADS;
    private static int _countTo = DEFAULT_COUNT_TO;
    private static int _maxIter = DEFAULT_MAX_ITER;
    private static boolean _isLocalMem = DEFAULT_IS_LOCAL_MEM;    
    private static BarrierType _barrierType;    
    private static String _matFile = "";    
    
    // TODO - for each thread add 2 arrays to save times of:
    // entering await and leaving await. then find max between all
    // t_enter[phase] cell to
    // find who got in last to the barrier and who was the last to leave
    // the last thread (the one who frees the barrier) is the thread with the
    // MAX enter time for phase i

    /**
     * @param args - first arg is the barrier name second arg is number of
     *        threads third arg is true iff local mem, false shared mem.
     */
    public static void main(String[] args) {       
        checkParams(args);
        boolean isCounter = (_countTo > 0);
        if (!isCounter){ 
            MatrixLoader.init(_matFile);
        }
//        MatrixLoader.init("mat1.txt");
//        MatrixLoader.getInstance().printMatricies();
        
//        MatricesMultiplier x = new MatricesMultiplier(MatrixLoader.getInstance().getMatrices(),8);
//        x.printMatrix(x.multAB(8));
//        if (true) return;
        
        
        StringBuffer buf = new StringBuffer();
        System.out.println(new Date() + " Starting tests, num of iterations: "
                + _maxIter + ", number of threads: " + _numOfThreads
                + ", isLocalMem: " + _isLocalMem + " count to: " + _countTo
                + "\n");

        
        for (int k = 0; k < _maxIter; k++) {

            if (args.length > 0) {
                buf.append(GeneralUtils.startThreads(_numOfThreads,
                        _barrierType, _isLocalMem, isCounter));
            } else {
                // GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.simpleBarrier, isLocalMem); // has deadlock
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.senseBarrier, isLocalMem, false));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.senseBarrierWithWait, isLocalMem));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.treeBarrier, isLocalMem));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.staticTreeBarrier, isLocalMem));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.lockBarrier, isLocalMem));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.cyclicBarrier, isLocalMem));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.binaryStaticTreeBarrier, isLocalMem, true));

                buf.append(GeneralUtils.startThreads(_numOfThreads,
                        BarrierType.linearSenseBarrierVolatile, _isLocalMem,
                        true));
//                buf.append(GeneralUtils.startThreads(_numOfThreads,
//                        BarrierType.linearSenseBarrierVolatileWithBackoff, _isLocalMem,
//                        true));
//                
//                buf.append(GeneralUtils.startThreads(_numOfThreads,
//                        BarrierType.senseBarrier, _isLocalMem, isCounter));
                
//                buf.append(GeneralUtils.startThreads(_numOfThreads,
//                        BarrierType.splittedSenseBarrier, _isLocalMem, true));
                
//                buf.append(GeneralUtils.startThreads(numberOfThreads,
//                        BarrierType.linearSenseBarrierVolatileWithBackoff,
//                        isLocalMem, true));
                
                // GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.simpleBarrier, !isLocalMem); // has deadlock
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.senseBarrier, !isLocalMem));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.treeBarrier, !isLocalMem));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.linearSenseBarrier, !isLocalMem));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.staticTreeBarrier, !isLocalMem));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.lockBarrier, !isLocalMem));
                // buf.append(GeneralUtils.startThreads(numberOfThreads,
                // BarrierType.cyclicBarrier, !isLocalMem));
                // System.out.println("\n" + new Date() + " tests ended, iter: "
                // + (k+1));
            }
        }
        System.out.println(new Date() + " - Done.");
    }

    /**
     * checks and fill params.
     * @param args
     */
    private static void checkParams(String[] args) {

        String ret = null;
        for (String param : args) {
            
            ret = checkAndReturnData(param, STR_LOCALMEM);
            if (ret != "") {
                try {
                    _isLocalMem = Boolean.parseBoolean(ret);
                } catch (NumberFormatException e) {
                    _isLocalMem = DEFAULT_IS_LOCAL_MEM;
                    System.out.println("fail to parse Memory type to check from"
                            + " arguments, using default: " + _isLocalMem);
                }
                continue;
            }
            
            ret = checkAndReturnData(param, STR_BARTYPE);
            if (ret != "") {
                try {
                    _barrierType = BarrierType.valueOf(ret);
                    continue;
                } catch (Exception e) {
                    System.exit(33);// bad name.
                }
            }
            
            ret = checkAndReturnData(param, STR_ITERS);
            if (ret != "") {
                try {
                    _maxIter = Integer.parseInt(ret);
                } catch (Exception e) {
                    _maxIter = DEFAULT_MAX_ITER;
                    System.out.println("fail to parse max iter, using default: "
                            + _maxIter);
                }
                continue;
            }
            
            ret = checkAndReturnData(param, STR_COUNTTO);
            if (ret != "") {
                try {
                    _countTo = Integer.parseInt(ret);
                } catch (Exception e) {
                    _countTo = DEFAULT_COUNT_TO;
                    System.out.println("fail to parse countTo, using default: "
                            + _countTo);
                }
                continue;
            }
            
            ret = checkAndReturnData(param, STR_NTHREADS);
            if (ret != "") {
                try {
                    _numOfThreads = Integer.parseInt(ret);
                } catch (Exception e) {
                    _numOfThreads = DEFAULT_NUM_OF_THREADS;
                    System.out.println("fail to parse " + STR_NTHREADS + "," +
                    		" using default: " + _numOfThreads);
                }
            }
            
            ret = checkAndReturnData(param, STR_MATRIX_FILE);
            if (ret != "") {
                try {
                    _matFile = ret;
                } catch (Exception e) {
                    _matFile = "";
                    System.out.println("fail to parse " + STR_MATRIX_FILE + "," +
                            " using default: \"\"");
                }
            }
        }
    }
    
    /**
     * @param param
     * @param paramPrefix
     * @return the value of the param if exists, otherwise empty string
     */
    private static String checkAndReturnData(String param, String paramPrefix) {
        if (param.toUpperCase().startsWith(paramPrefix)) {
                return param.substring(paramPrefix.length());
        }
        return "";
    }
    
    public static final int getCountTo() {
        return _countTo;
    }
    
}
